use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;

use crate::instruction::instruction::InstructionFactory;
use crate::vm::VM;
use crate::class::vm_class::{VMClass, VMClassMethod};
use crate::class::vm_class_attr::VMClassAttrEnum;
use crate::vm_val::{NULL_REF, VMValDouble, VMValEnum, VMValInteger, VMValLong, VMValRef};

pub struct VMThread {
    pub max_stack_size: i16,
    pub stack: RefCell<Vec<Rc<RefCell<VMFrame>>>>,
    pub vm: Rc<VM>,
    // pub method: &'a VMClassMethod,
}


impl VMThread {
    pub fn new_thread(vm: Rc<VM>) -> Result<VMThread, String> {
        return Ok(VMThread {
            max_stack_size: 1024,
            stack: RefCell::new(vec![]),
            vm,
            // method,
        });
    }

    pub fn start(&mut self) -> i32 {
        loop {
            let frame = {
                let stack = self.stack.borrow();
                if stack.len() == 0 {
                    break;
                }
                stack.last().unwrap().clone()
            };
            let opcode = {
                let mut rw_frame = frame.borrow_mut();
                let opcode = rw_frame.read_u8();
                opcode
            };

            let mut inst = {
                let mut inst = InstructionFactory::create_instruction(opcode);
                let mut ro_frame = frame.borrow();
                println!("{}#{}#{}#opcode:{},{}", &ro_frame.current_vm_class.name, &ro_frame.method.name,
                         &ro_frame.method.descriptor.class_method_descriptor_str, opcode, inst.to_string());
                inst
            };

            inst.read_operands(frame.clone());
            inst.exec(frame.clone(), self);
        }
        return 0;
    }
}

pub struct VMFrame {
    pub pc: usize,
    pub code: Rc<RefCell<Vec<u8>>>,
    pub method: Rc<VMClassMethod>,
    pub current_vm_class: Rc<VMClass>,
    pub operand_stack: RefCell<Vec<VMValEnum>>,
    pub local_vars: RefCell<HashMap<u8, VMValEnum>>,
}


macro_rules! frame_get_val_from_local_vars {
    ($frame:expr, $index:expr, $enumname:ident) => ({
        let ro_local_vars = $frame.local_vars.borrow();
        let pop_option = ro_local_vars.get($index);
        if pop_option.is_none() {
            panic!("{},{}, 变量不存在", file!(), line!());
        }
        return match pop_option.unwrap() {
            VMValEnum::$enumname { val } => {
                $enumname { num: val.num }
            }
            _ => {
                panic!("{},{}, 类型匹配异常", file!(), line!());
            }
        };
    });
}

macro_rules! frame_pop {
    ($frame:expr, $enumname:ident) => ({
        let pop_option = $frame.operand_stack.borrow_mut().pop();
        if pop_option.is_none() {
            panic!("{},{}, 操作数栈为空", file!(), line!());
        }
        match pop_option.unwrap() {
            VMValEnum::$enumname { val } => {
                val
            }
            _ => {
                panic!("{},{}, 类型匹配异常", file!(), line!());
            }
        }
    });
}

impl VMFrame {
    pub fn pop_long_from_stack(&self) -> VMValLong {
        return frame_pop!(self, VMValLong);
    }

    pub fn pop_integer_from_stack(&self) -> VMValInteger {
        let pop_option = self.operand_stack.borrow_mut().pop();
        if pop_option.is_none() {
            panic!("{},{}, 操作数栈为空", file!(), line!());
        }
        let val_enum = pop_option.unwrap().clone();
        return VMValEnum::convert2integer(&val_enum);
    }

    pub fn pop_ref_from_stack(&self) -> VMValRef {
        let pop_option = self.operand_stack.borrow_mut().pop();
        if pop_option.is_none() {
            panic!("{},{}, 操作数栈为空", file!(), line!());
        }
        let val_enum = pop_option.unwrap().clone();
        return match val_enum {
            VMValEnum::VMValRef { val } => { val }
            _ => { panic!("{},{}, 类型匹配异常", file!(), line!()); }
        };
    }


    pub fn get_long_from_local_var(&self, index: u8) -> VMValLong {
        return frame_get_val_from_local_vars!(self, &index, VMValLong);
    }

    pub fn get_int_from_local_var(&self, index: u8) -> VMValInteger {
        let ro_local_vars = self.local_vars.borrow();
        let val_option = ro_local_vars.get(&index);
        if val_option.is_none() {
            panic!("{},{}, 变量不存在", file!(), line!());
        }
        let val = val_option.unwrap().clone();
        return VMValEnum::convert2integer(&val);
    }


    pub fn get_double_from_local_var(&self, index: u8) -> VMValDouble {
        let ro_local_vars = self.local_vars.borrow();
        let val_option = ro_local_vars.get(&index);
        if val_option.is_none() {
            panic!("{},{}, 变量不存在", file!(), line!());
        }
        let val = val_option.unwrap().clone();
        return VMValEnum::convert2double(&val);
    }


    pub fn get_ref_from_local_var(&self, index: u8) -> VMValRef {
        return frame_get_val_from_local_vars!(self, &index, VMValRef);
    }


    pub fn read_u8(&mut self) -> u8 {
        let old_pc = self.pc;
        self.pc = self.pc + 1;
        return self.code.borrow()[old_pc];
    }

    pub fn read_u16(&mut self) -> u16 {
        return u16::from_be_bytes([self.read_u8(), self.read_u8()]);
    }

    pub fn set_pc(&mut self, new_pc: usize) {
        self.pc = new_pc;
    }

    pub fn pc(&self) -> usize {
        return self.pc;
    }
}

